/*
 * Copyright 2014 by État de Genève (Switzerland)
 * All rights reserved.
 *
 * Author: ELCA Informatique SA
 */
define(['moment', 'i18n', 'services/appConfig', 'services/layout', 'common/noPoursuite'],
    function (moment, i18n, appConfig, L, noPoursuite) {

        var _null = '-';
        var _invalidDate = i18n.t('cms:format.invalidDate');

        // available formats in formatter service
        return {
            // do nothing (returns input as is)
            '_default': defaultFormatFunc,
            // return '-' if string is null or empty
            'replaceIfEmpty': replaceIfEmpty,

            // 'John Doe' or ['John', 'Doe'] or { ...: 'John', ...: 'Doe' } => 'John DOE'
            'fullname': fullnameFormatFunc,

            // 1395841059153 => 26.03.2014
            'date': formatDate.bind(undefined, 'DD.MM.YYYY'),
            // 1395841059153 => 26.03
            'dateDM': formatDate.bind(undefined, 'DD.MM'),
            // 1395841059153 => 2014
            'dateY': formatDate.bind(undefined, 'YYYY'),
            // 1395841059153 => 26.03.2014 20:37
            'dateHM': formatDate.bind(undefined, 'DD.MM.YYYY HH:mm'),
            // Forme abrégée (VISA): SYSTEM => SYS
            'visa': visaFormatFunc,

            // replaces \n with <br /> (for addresses in list tables)
            'address': addressFormatFunc,

            // XXX => <i class="op-i op-i-XXX"></i>
            'icon': opusIconFunc,
            // XXX => <i class="mi mi-XXX"></i>
            'minicon': miniconFunc,
            // PersonnePhyisque, PersonneMorale, Communaute => <i class="op-i op-i-XXX"></i>
            'tiersIcon': tiersIconFunc,
            // OK, Attention, KO => <i class="op-i op-i-XXX"></i>
            'messagesIcon': messagesIconFunc,
            // Larger Icons for OK, Attention, KO => <i class="op-i op-i-XXX"></i>
            'messagesLargeIcon': messagesLargeIconFunc,
            // Oui, Non, Partiel => <i class="op-i op-i-XXX"></i>
            'identifierIcon': identifierIconFunc,

            'entityTypeStr': entityType,

            // 123456789.469 => 123'456'789.47
            'currency': currencyFormatFunc,
            // 0.55 => 55
            'percent': percentFormatFunc,

            // uses the enums retrieved in appConfig to translate Java enums values
            'keyLabel': enumTransformFunc,

            // if (undefined, null, 0, '', false) => 'Non'
            // else => 'Oui'
            'bool': boolFormatFunc,

            // displays an icon instead of a text, and a tooltip with the on hover
            'tooltip': toolTipFunc,

            'filesize': sizeFunc,

            'noPoursuite': noPoursuiteFunc,
            'statut': statutFunc,
            'addressObject': addressObjectFunc
        };

        /**
         * Format statut object to statut string,
         */
        function statutFunc(value) {
            // a statut object must be bound to an object of type :
            // { code: ..., nom: ...}
            if (!value) {
                return '-';
            } else {
                value = ko.unwrap(ko.mapping.toJS(value));
                return value.code + ' - ' + value.nom;
            }
        }

        /**
         * Format tier address object to string,
         */
        function addressObjectFunc(value) {
            // a address object must be bound to an object of type :
            // { rue: ..., numero: ..., codePostal: ..., ville: ...}
            if (!value) {
                return '-'
            } else {
                value = ko.unwrap(ko.mapping.toJS(value));
                return addressFormatFunc(defaultFormatFunc(value.rue) + ', '
                    + defaultFormatFunc(value.numero) + '\n'
                    + defaultFormatFunc(value.codePostal) + ' '
                    + defaultFormatFunc(value.ville));
            }
        }

        /**
         * Checks and format an input string to a valid numero poursuite
         */
        function noPoursuiteFunc(value) {
            if(!noPoursuite.validate(value)) {
                return _null;
            }

            return noPoursuite.reformat(value);
        }

        /**
         * Format value as ko/Mo/Go units
         */
        function sizeFunc(value) {
            value = parseInt(value, 10);
            if (_.isNaN(value)) {
                return _null;
            }

            var unit = 'o';
            if (value > 1000) {
                value = value / 1000;
                unit = 'ko';
                if (value > 1000) {
                    value = value / 1000;
                    unit = 'Mo';
                    if (value > 1000) {
                        value = value / 1000;
                        unit = 'Go';
                    }
                }
            }
            return Math.round(value).toString() + unit;
        }

        /**
         * Format value as an icon with a popover on mouse hover
         */
        function toolTipFunc(value) {
            if (!value) {
                return '';
            }

            L.hasPopovers();

            return '<i data-toggle="popover" data-container="body" data-trigger="hover" data-placement="bottom" data-content="' + value + '" class="mi mi-communication-83"></i>';
        }

        /**
         * Get the label corresponding to an enum key
         * params should have a listId property containing the name of the enum
         */
        function enumTransformFunc(value) {
            if (!value) {
                return _null;
            }

            if (typeof value === 'string') {
                return value;
            }

            if (value.label) {
                return value.label;
            }

            if (value.key) {
                return value.key;
            }

            return _null;
        }

        /**
         * transform value in string (do no processing)
         */
        function defaultFormatFunc(value) {
            if (typeof value !== 'undefined' && value !== null) {
                return value.toString();
            } else {
                return '';
            }
        }

        /**
         * return '-' if string is null or empty
         */
        function replaceIfEmpty(value) {
            if (typeof value !== 'undefined' && value !== null && value !== '') {
                return value.toString();
            } else {
                return _null;
            }
        }

        /**
         * return a fullname from a string, an object, or an array
         * if value is an object, params.firstname contains the 'firstname' property of value,
         * and params.lastname contains the name of 'lastname' property of value..... (look at the code it's easier...)
         */
        function fullnameFormatFunc(value, params) {

            if (!value) {
                return _null;
            }

            if (typeof value === 'string') {
                var names = value.split(' ');
                names[names.length - 1] = names[names.length - 1].toUpperCase();
                return names.join(' ');
            }

            if (value instanceof Array && value.length >= 2) {
                value[value.length - 1] = value[value.length - 1].toUpperCase();
                return value.join(' ');
            } else if (value.length < 2) {
                return _null;
            }

            if (params && params.firstname && params.lastname
                && value[params.lastname] && value[params.firstname]) {

                return value[params.firstname] + ' ' + value[params.lastname].toUpperCase();
            }

            return _null;
        }

        /**
         * helper function to format a date
         */
        function formatDate(format, value) {
            if (typeof value === 'undefined' || value === null || value === '') {
                return _null;
            }
            var d = moment(value);
            if (!d.isValid()) {
                return _invalidDate;
            }
            return d.format(format);
        }

        /**
         * for list tables, converts name to visa
         */
        function visaFormatFunc(value) {
            // TODO [qat, 1] : Ask for the exactly abbreviated format, currently using first 3 characters
            if (value) {
                return value.substring(0, 3);
            } else {
                return '';
            }
        }

        /**
         * for list tables, converts addresses with \n in addresses with <br />
         */
        function addressFormatFunc(value) {
            if (value) {
                return value.replace('\n', '<br />');
            } else {
                return _null;
            }
        }

        /**
         * get an opus icon from a string value (opus icons are not from the minicon library)
         */
        function opusIconFunc(value) {
            return '<i class="op-i op-i-' + value + '"></i>';
        }

        /**
         * get a minicon from a string
         */
        function miniconFunc(value) {
            return '<i class="mi mi-' + value + '"></i>';
        }

        /**
         * Maps a tiers type to a corresponding icon
         */
        function tiersIconFunc(value) {
            var map = {
                'PersonnePhysique': 'users-5',
                'PersonneMorale': 'places-13',
                'Communaute': 'users-23'
            };

            return '<i class="mi mi-' + map[value] + '"></i>';
        }

        /**
         * Maps a Indicateur global type to a corresponding icon
         */
        function messagesIconFunc(value) {
            var map = {
                'OK': 'ok',
                'Attention': 'warning',
                'KO': 'critical'
            };

            return '<i class="op-i op-i-' + map[value] + '"></i>';
        }

        /**
         * Maps a Indicateur global type to a corresponding larger icon
         */
        function messagesLargeIconFunc(value) {
            var map = {
                'OK': 'ok',
                'Attention': 'warning',
                'KO': 'critical'
            };

            return '<i class="op-i-20-x-20 op-i-' + map[value] + '"></i>';
        }

        /**
         * Maps a IndentificationTier type to a corresponding icon
         */
        function identifierIconFunc(value) {
            var map = {
                'Oui': 'oui',
                'Partiel': 'partiel',
                'Non': 'non'
            };

            return '<i class="op-i-15-x-15 op-i-' + map[value] + '"></i>';
        }

        /**
         * Transform an entity type from the server in a more human readable text
         * Uses i18n to build the text from the given raw type
         */
        function entityType(value) {
            if (!value) {
                return _null;
            }
            return i18n.t('cms:types.' + value.toLowerCase());
        }

        /**
         * returns the value * 100 with no more than 3 decimals
         */
        function percentFormatFunc(value) {
            // round percent at 4 digit max
            value = Math.round(value * 100 * 10000) / 10000;

            return value.toString() + '%';
        }

        /**
         * Round the value to 2 decimals and add ' between thousands
         */
        function currencyFormatFunc(value) {

            var withoutDecimals = Math.floor(Math.abs(value));

            // take the 2 decimals
            var decimals = Math.round((Math.abs(value) - withoutDecimals) * 100);
            var decimalStr = decimals.toString();
            if (decimals < 10) {
                decimalStr = '0' + decimalStr;
            }

            /**
             * Recursively transform a number in group of number separated with '
             */
            function toFrCh(number) {

                // left part (remain to be processed in next recursion)
                var l = Math.floor(number / 1000);
                // right part
                var r = number - (l * 1000);

                // transform right part in string and add missing zeros if right part is only 1 or 2 digits
                if (r < 100) {
                    if (r < 10) {
                        r = '0' + r;
                    }
                    r = '0' + r;
                }

                // if there's still a left part, continue processing
                if (l !== 0) {
                    return toFrCh(l) + '\'' + r;
                } else {
                    return number;
                }
            }

            // if transformation is 0, result is 0 in string
            var formatted = (toFrCh(withoutDecimals) || '0');

            // change sign if negative
            if (value < 0) {
                formatted = '-' + formatted;
            }

            // add suffix
            return formatted + '.' + decimalStr;
        }

        /**
         * Display a boolean value in french
         */
        function boolFormatFunc(value) {
            return value ? i18n.t('cms:yes') : i18n.t('cms:no');
        }
    });